import logging
import json
from datetime import datetime
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db import transaction
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.authentication import TokenAuthentication
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from api_test.common_exception.exceptions import ParamsMissedException, ParamsIsNullException, ParamsTypeErrorException
from api_test.models import MockServiceInfo
from api_test.common.decorator import catch_exception
from api_test.serializers import MockServiceInfoSerializer, MockServiceInfoDeserializer
from api_test.common.api_response import JsonResponse

logger = logging.getLogger(__name__)  # 这里使用 __name__ 动态搜索定义的 logger 配置，这里有一个层次关系的知识点。


class GetMockRequest(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def get(self, request, address=None):
        """
        get请求
        :param request:
        :param address:
        :return:
        """
        address = '/' + address
        mock = MockServiceInfo.objects.filter(address=address)
        if not mock:
            return JsonResponse(code="999995", msg=f"mock接口{address}未创建!")
        if not mock[0].status:
            return JsonResponse(code="999995", msg=f"mock接口{address}已禁用,请开启!")
        return JsonResponse(code='999999', msg=f'mock接口{address}调用成功', data=json.loads(mock[0].data))


class AddMockRequest(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        for param in ['address', 'data']:
            try:
                if not data[param]:
                    raise ParamsIsNullException(param)
                if param == 'address':
                    if not isinstance(data[param], str):
                        raise ParamsTypeErrorException(param)
                else:
                    if not isinstance(data[param], dict):
                        raise ParamsTypeErrorException(param)
            except KeyError:
                raise ParamsMissedException(param)

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['data', 'address'],
        properties={
            'data': openapi.Schema(type=openapi.TYPE_OBJECT, description="mock接口响应结果"),
            'address': openapi.Schema(type=openapi.TYPE_STRING, description="mock接口地址")
        },
    ))
    @catch_exception
    def post(self, request):
        """
        post请求
        :param request:
        :return:
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        mock = MockServiceInfo.objects.filter(address=data['address'])
        if mock:
            return JsonResponse(code="999995", msg=f"mock接口:{data['address']}已存在!")
        data['data'] = json.dumps(data['data'])
        serializer = MockServiceInfoDeserializer(data=data)
        if serializer.is_valid():
            serializer.save(data=data['data'], address=data['address'], create_time=datetime.now())
            return JsonResponse(code="999999", data={'mockId': serializer.data.get('id')}, msg='mock接口创建成功')
        return JsonResponse(code="999995", msg="mock接口创建失败!")


class MockList(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @swagger_auto_schema(manual_parameters=[
        openapi.Parameter(name="address", in_=openapi.IN_QUERY, type=openapi.TYPE_STRING, description="mock接口地址"),
        openapi.Parameter(name="page_size", in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description="单页显示数量"),
        openapi.Parameter(name="page", in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, description="页码")

    ])
    def get(self, request):
        """
        get请求
        :param request:
        :return:
        获取mock接口列表
        """
        page_size = request.GET.get("page_size")
        page = request.GET.get("page")
        address = request.GET.get('address')
        if address:
            obj = MockServiceInfo.objects.filter(address=address)
            if not obj:
                return JsonResponse(code='999995', msg=f'mock接口:{address}不存!')
            if obj.filter(status=False):
                return JsonResponse(code='999995', msg=f'mock接口:{address}已禁用,请开启!')
            serializer = MockServiceInfoSerializer(obj, many=True)
            return JsonResponse(code='999999', msg=f'获取mock接口:{address}成功!', data={"data": serializer.data})
        obi = MockServiceInfo.objects.all().order_by('id')
        if not page and not page_size:
            serializer = MockServiceInfoSerializer(obi, many=True)
            return JsonResponse(code='999999', msg=f'获取mock接口列表成功!', data={"data": serializer.data})
        elif page_size and page:
            try:
                page_size = int(page_size)
                page = int(page)
            except (TypeError, ValueError):
                return JsonResponse(code="999985", msg="page and page_size must be integer!")
            paginator = Paginator(obi, page_size)  # paginator对象
            page_sizes = paginator.num_pages  # 总页数
            total = paginator.count
            try:
                obm = paginator.page(page)
            except PageNotAnInteger:
                obm = paginator.page(1)
            except EmptyPage:
                obm = paginator.page(paginator.num_pages)
            serializer = MockServiceInfoSerializer(obm, many=True)
            return JsonResponse(code='999999', msg=f'获取mock接口列表成功!', data={"data": serializer.data,
                                "page_sizes": page_sizes, "total": total})
        else:
            return JsonResponse(code="999995", msg="page and page_size must be exist at the same time!")


class EditMockRequest(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        for param in ['address', 'data', 'id']:
            try:
                if not data[param]:
                    raise ParamsIsNullException(param)
                if param == 'address':
                    if not isinstance(data[param], str):
                        raise ParamsTypeErrorException(param)
                elif param == 'data':
                    if not isinstance(data[param], dict):
                        raise ParamsTypeErrorException(param)
                elif param == 'id':
                    if not isinstance(data[param], int):
                        raise ParamsTypeErrorException(param)
            except KeyError:
                raise ParamsMissedException(param)

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['id', 'data', 'address', 'status'],
        properties={
            'id': openapi.Schema(type=openapi.TYPE_INTEGER, description="mock接口id"),
            'data': openapi.Schema(type=openapi.TYPE_OBJECT, description="mock接口响应结果"),
            'address': openapi.Schema(type=openapi.TYPE_STRING, description="mock接口地址"),
            'status': openapi.Schema(type=openapi.TYPE_BOOLEAN, description="mock接口状态")
        },
    ))
    @catch_exception
    def post(self, request):
        """
        post请求
        :param request:
        :return:
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        data['data'] = json.dumps(data['data'])
        mock_id = data.get('id')
        mock = MockServiceInfo.objects.filter(id=mock_id)
        if not mock:
            return JsonResponse(code="999995", msg=f"mock接口:{mock_id}不存在!")
        if MockServiceInfo.objects.filter(address=data['address']).exclude(id=mock_id):
            return JsonResponse(code="999995", msg=f"mock接口:{data['address']}已存在,请修改!")
        serializer = MockServiceInfoDeserializer(data=data)
        if serializer.is_valid():
            with transaction.atomic():  # 执行错误后，帮助事务回滚
                serializer.update(instance=mock[0], validated_data=data)
            return JsonResponse(code="999999", data={'mockId': data.get('id')}, msg='mock接口编辑成功')
        return JsonResponse(code="999995", msg="mock接口编辑失败!", data=serializer.errors)


class DeleteMockRequest(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    @catch_exception
    def parameter_check(self, data):
        try:
            id_list = data['idList']
            if not id_list:
                raise ParamsIsNullException('idList')
            if not isinstance(id_list, list):
                raise ParamsTypeErrorException('idList')
            for i in id_list:
                if not isinstance(i, int):
                    raise ParamsTypeErrorException('idList列表元素')
        except KeyError:
            raise ParamsMissedException('idList')

    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        required=['idList'],
        properties={
            'idList': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                                     description="待禁用mock id列表"),
        },
    ))
    @catch_exception
    def post(self, request):
        """
        post请求
        :param request:
        :return:
        """
        data = JSONParser().parse(request)
        result = self.parameter_check(data)
        if result:
            return result
        id_list = data.get('idList')
        mock = MockServiceInfo.objects.filter(id__in=id_list)
        if not mock:
            return JsonResponse(code="999995", msg=f"idList:{id_list}不存在!")
        if len(id_list) > mock.count():
            return JsonResponse(code="999995", msg=f"idList:{id_list}中含有不存在的id!")
        if len(id_list) > mock.filter(status=True).count():
            return JsonResponse(code="999995", msg=f"idList:{id_list}中含有已禁用的id!")
        with transaction.atomic():
            mock.update(status=False)
            return JsonResponse(code="999999", msg="mock接口禁用成功!")
